﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.IO;
using System.Collections.ObjectModel;
using ICSharpCode;
using LumenWorks.Framework.IO.Csv;
using System.Threading;

using Common;


namespace ForexHistory
{
    public class CurrencyPairHistory
    {

        #region Vars
        DateCompare _dateComp = new DateCompare();
        FullCompare _fullComp = new FullCompare();

        Tick _lastReturned;


        private int _start;
        private int _end;

        private int _duplicateCount = 0;

        public int DuplicateCount
        {
            get { return _duplicateCount; }
            set { _duplicateCount = value; }
        }

        public int TotalYears
        {
            get { return ((_end - _start) + 1); }

        }

        private int _expectedTicks = 0;

        public int ExpectedTicks
        {
            get { return _expectedTicks; }
        }

        public int ActualTicks
        {
            get
            {
                int res = 0;

                DateTime _starting = new DateTime(_start, 1, 1);

                DateTimeArrayIndex _i = new DateTimeArrayIndex(_starting, _end);

                while (!_i.DateTraversalComplete)
                {
                    for (int x = 0; x < _ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length; ++x)
                    {
                        if (_ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].RateAskRaw != 0 && _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].RateBidRaw != 0)
                            ++res;
                    }

                    _i.AddHours(1);
                }

                /*
                for (int y = 0; y < TotalYears; ++y)
                {
                    for (int m = 0; m < 12; ++m)
                    {
                        for (int d = 0; d < 31; ++d)
                        {
                            for (int h = 0; h < 24; ++h)
                            {
                                for (int x = 0; x < _ticks[y, m, d, h].Length; ++x)
                                {
                                    if (_ticks[y, m, d, h][x].RateAsk != 0 && _ticks[y, m, d, h][x].RateBid != 0)
                                        ++res;
                                }
                            }
                        }
                    }
                }*/

                return res;

            }
        }

        private object _tickLock = new object();

        private bool _loadingComplete = false;
        private object _flagLock = new object();

        public bool LoadingComplete
        {
            get
            {
                lock (_flagLock)
                {
                    return _loadingComplete;
                }
            }
            set
            {
                lock (_flagLock)
                {
                    _loadingComplete = value;
                }
            }
        }



        private CurrencyPair _pair;

        public CurrencyPair Pair
        {
            get { return _pair; }
            set { _pair = value; }
        }

        private Tick[, , ,][] _ticks;
        private int[, , ,] _idx;

        public Tick[, , ,][] Ticks
        {
            get { return _ticks; }
            set { _ticks = value; }
        }

        private ArrayList _loadQueue = new ArrayList();
        #endregion

        public CurrencyPairHistory(CurrencyPair Pair, int Start, int End)
        {
            _pair = Pair;
            _start = Start;
            _end = End;
        }

        public void InitializeArray()
        {
            if (null == _ticks)
            {
                int _currentYear = _start;

                _ticks = new Tick[TotalYears, 12, 31, 24][];
                _idx = new int[TotalYears, 12, 31, 24];

                DateTime _current = new DateTime(_start, 1, 1);
                DateTime _endDate = new DateTime(_end + 1, 1, 1);

                DateTimeArrayIndex _i = new DateTimeArrayIndex(_current, _end);

                while (!_i.DateTraversalComplete)
                {

                    _ticks[_i.Year, _i.Month, _i.Day, _i.Hour] = new Tick[3600];
                    _idx[_i.Year, _i.Month, _i.Day, _i.Hour] = 0;

                    _i.AddHours(1);
                }
            }
        }

        public void AddTick(DateTime time, int bid, int ask, int Order)
        {
            try
            {

                Tick _toAdd = new Tick();

                _toAdd.TickTime = time;
                _toAdd.RateBidRaw = bid;
                _toAdd.RateAskRaw = ask;
                _toAdd.Order = Order;

                DateTimeArrayIndex _i = new DateTimeArrayIndex(time, _end);

                if (_idx[_i.Year, _i.Month, _i.Day, _i.Hour] == (_ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length - 1))
                {
                    InvokeErrorEvent("Resizing Array");
                    Array.Resize<Tick>(ref _ticks[_i.Year, _i.Month, _i.Day, _i.Hour], _ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length + 1000);
                    InvokeErrorEvent("Resize complete");
                }

                // = tests Time & Rates
                // <> tests date only
                int _r = Array.BinarySearch<Tick>(_ticks[_i.Year, _i.Month, _i.Day, _i.Hour], _toAdd);


                //Not found
                if (_r < 0)
                {
                    _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_idx[_i.Year, _i.Month, _i.Day, _i.Hour]] = _toAdd;
                    ++_idx[_i.Year, _i.Month, _i.Day, _i.Hour];
                }
                else
                {
                    ++_duplicateCount;


                    Tick _orig = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_r];

                    if (!(_orig.TickTime == _toAdd.TickTime && _orig.RateAskRaw == _toAdd.RateAskRaw && _orig.RateBidRaw == _orig.RateBidRaw))
                    {
                        InvokeErrorEvent("________________________________________________________________________");
                        InvokeErrorEvent("ERROR!  These ticks were flagged as duplicates, but they are not equal!");
                        InvokeErrorEvent("ORIGINAL  : " + _orig.TickTime.ToString() + " / " + _orig.RateBidRaw.ToString() + " / " + _orig.RateAskRaw.ToString());
                        InvokeErrorEvent("DUPLICATE: " + _toAdd.TickTime.ToString() + " / " + _toAdd.RateBidRaw.ToString() + " / " + _toAdd.RateAskRaw.ToString());
                    }




                }


            }
            catch (Exception ex)
            {
                InvokeErrorEvent(ex.Message);
            }
        }

        public void LoadComplete()
        {

            int _nullPosition = -1;
            InvokeStatusEvent("Trimming Tick Arrays...");

            DateTime _starting = new DateTime(_start, 1, 1);

            DateTimeArrayIndex _i = new DateTimeArrayIndex(_starting, _end);

            while (!_i.DateTraversalComplete)
            {
                for (int x = 0; x < _ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length; ++x)
                {
                    if (_ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].RateAskRaw == 0 && _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].RateBidRaw == 0)
                    {
                        _nullPosition = x;
                        break;
                    }
                }

                if (_nullPosition != -1)
                {

                    Array.Resize<Tick>(ref _ticks[_i.Year, _i.Month, _i.Day, _i.Hour], _nullPosition);

                }

                _nullPosition = -1;

                _i.AddHours(1);
            }

            GC.Collect();
            InvokeStatusEvent("Trim Complete");
        }

        public void AddToQueue(string csv_path)
        {
            _loadQueue.Add(csv_path);
        }

        public void Sort()
        {

            DateTime _starting = new DateTime(_start, 1, 1);

            DateTimeArrayIndex _i = new DateTimeArrayIndex(_starting, _end);

            while (!_i.DateTraversalComplete)
            {
                Array.Sort<Tick>(_ticks[_i.Year, _i.Month, _i.Day, _i.Hour], CompareTickTime);

                _i.AddHours(1);
            }
            /*
            for (int y = 0; y < TotalYears; ++y)
            {
                for (int m = 0; m < 12; ++m)
                {
                    for (int d = 0; d < 31; ++d)
                    {
                        for (int h = 0; h < 24; ++h)
                        {
                            Array.Sort<Tick>(_ticks[y, m, d, h], CompareTickTime);
                        }
                    }
                }
            }*/

        }

        public int ProcessFileFromMemory(MemoryStream ms, string name)
        {
            int _lineCount = 0;

            if (ms != null)
            {
                ms.Position = 0;
                using (TextReader reader = new StreamReader(ms, Encoding.ASCII))
                {
                    ms.Position = 0;

                    using (CsvReader csv = new CsvReader(new StreamReader(ms), true))
                    {
                        _lineCount = ProcessFile(csv, name);
                    }
                }
            }
            else
            {
                InvokeErrorEvent("MemoryStream ms is null: " + name);
            }

            _expectedTicks += _lineCount;

            return _lineCount;
        }

        private int ProcessFile(CsvReader csv, string FileName)
        {
            int i = 0;

            int fieldCount = csv.FieldCount;

            string[] headers = csv.GetFieldHeaders();

            

            while (csv.ReadNextRecord())
            {
                #region
                try
                {

                    int _pidx = PairColumnIndex(headers);
                    int _tidx = TickTimeColumnIndex(headers);
                    int _bidx = RateBidColumnIndex(headers);
                    int _aidx = RateAskColumnIndex(headers);


                    if (_pidx == -1 || _tidx == -1 || _bidx == -1 || _aidx == -1)
                    {
                        throw new Exception("Unable to locate columns for file " + FileName);
                    }

                    string _pair = Shared.NormalizeCurrencyPairName(csv[_pidx]);
                    string _time = csv[_tidx];
                    string _bid = csv[_bidx];
                    string _ask = csv[_aidx];

                    if (_pair != null &&
                        _time != null &&
                        _bid != null &&
                        _ask != null &&
                        _pair.Trim() != "" &&
                        _time.Trim() != "" &&
                        _bid.Trim() != "" &&
                        _ask.Trim() != "")
                    {

                        decimal _bi;


                        if (!decimal.TryParse(_bid, out _bi))
                            _bi = -1;

                        if (_bi == -1)
                        {
                            InvokeErrorEvent("Conversion Error: " + _bid);
                            continue;
                        }

                        decimal _ai;
                        if (!decimal.TryParse(_ask, out _ai))
                            _ai = -1;

                        if (_ai == -1)
                        {
                            InvokeErrorEvent("Conversion Error: " + _bid);
                            continue;
                        }

                        DateTime _d;
                        if (!DateTime.TryParse(_time, out _d))
                        {
                            InvokeErrorEvent("Conversion Error: " + _time);
                            continue;
                        }

                        lock (_tickLock)
                        {
                            this.AddTick(_d, Convert.ToInt32(_bi * 10000), Convert.ToInt32(_ai * 10000), i);
                        }

                        ++i;

                    }
                    else
                    {
                        break;
                    }

                }
                catch (Exception ex)
                {
                    InvokeErrorEvent(ex.Message);
                    InvokeErrorEvent("Error reading CSV Line. File " + FileName + " Line " + i.ToString());
                    break;
                }
                #endregion
            }


            return i;
        }

        public void Verify()
        {
            DateTime _previous = DateTime.Now;
            TimeSpan _largest = new TimeSpan(0, 0, 0);
            DateTime _a = DateTime.Now, _b = DateTime.Now;
            TimeSpan _total = new TimeSpan(0, 0, 0);
            bool _isPreviousSet = false;


            DateTime _starting = new DateTime(_start, 1, 1);

            DateTimeArrayIndex _i = new DateTimeArrayIndex(_starting, _end);

            while (!_i.DateTraversalComplete)
            {
                for (int x = 0; x < _ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length; ++x)
                {
                    if (!_isPreviousSet)
                    {
                        _previous = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime;
                        _isPreviousSet = true;
                        continue;
                    }

                    _total += (_ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime - _previous);

                    if (_previous < _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime || _previous == _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime)
                    {

                        if (((TimeSpan)(_ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime - _previous)) > _largest)
                        {
                            _largest = ((TimeSpan)(_ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime - _previous));
                            _a = _previous;
                            _b = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime;
                        }
                    }
                    else
                    {
                        InvokeErrorEvent("Tick times not sorted properly - _last: " + _previous.ToString() + " Tick[" + x.ToString() + "]: "/* + _ticks[x].TickTime*/);
                        return;
                    }

                    _previous = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime;

                    if (_ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].RateBidRaw == 0)
                        InvokeErrorEvent("RateBid was 0!");

                    if (_ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].RateAskRaw == 0)
                        InvokeErrorEvent("RateAsk was 0!");
                }

                _i.AddHours(1);
            }

            InvokeStatusEvent(_pair.ToString() + ": The largest gap of time was " + _largest.Days + " Days " + _largest.Hours + ":" + _largest.Minutes + ":" + _largest.Seconds + ":" + _largest.Milliseconds + ", between " + _a.ToString() + " and " + _b.ToString());

            InvokeStatusEvent(_pair.ToString() + ": Average gap time " + ((_total.TotalMilliseconds / this.ActualTicks) / 1000).ToString() + " Seconds");

            InvokeLoadCompleteEvent();
        }

        private static int CompareTickTime(Tick x, Tick y)
        {
            if (x.TickTime == y.TickTime)
                return 0;
            else if (x.TickTime < y.TickTime)
                return -1;
            else
                return 1;
        }


        public Tick GetTick(DateTime Time)
        {
            // Return the tick that immediately precedes Time
            Tick res = new Tick();

            res.TickTime = Time;

            DateTime _endDate = new DateTime(_end + 1, 1, 1);

            DateTimeArrayIndex _i = new DateTimeArrayIndex(Time, _end);

            bool _exactMatch = false;

            // Nothing to return
            // this is bad
            while (_ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length == 0)
            {
                _i.SubtractHours(1);

                if (_i.DateObject.Year < _start)
                {
                    throw new Exception("No available Tick Data found for " + this.Pair.ToString());
                }

            }



            //  return new Tick();

            int _r = Array.BinarySearch<Tick>(_ticks[_i.Year, _i.Month, _i.Day, _i.Hour], res, _dateComp);

            /*
             * Return Value
             *  Type: System.Int32
             *  The index of the specified value in the specified array, if value is found. If value is not found and value is less than one or more elements in array, 
             *  a negative number which is the bitwise complement of the index of the first element that is larger than value. If value is not found and value is greater 
             *  than any of the elements in array, a negative number which is the bitwise complement of (the index of the last element plus 1).
            */

            // How many matches?

            bool _checkForMultiples = true;
            int _sourceIndex = -1;

            if (_r >= 0)
            {
                // Exact Match

                res = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_r];

                _sourceIndex = _r;
                _exactMatch = true;
            }
            else if (Math.Abs((~_r)) < _ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length)
            {
                //  If value is not found and value is less than one or more elements in array, 
                //  a negative number which is the bitwise complement of the index of the first element that is larger than value.

                _r = Math.Abs(~_r);
                // _r == 0?
                // _r == 1?
                if (_r == 0)
                {
                    // get last tick from previous hour or day or month or year
                    _i.SubtractHours(1);

                    res = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length - 1];

                    _checkForMultiples = false;

                    _i.AddHours(1);
                }
                else
                {
                    res = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_r - 1];

                    _sourceIndex = _r - 1;
                }
            }
            else
            {
                // If value is not found and value is greater 
                // than any of the elements in array, a negative number which is the bitwise complement of (the index of the last element plus 1).

                // Return the last array element
                res = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length - 1];

                _sourceIndex = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length - 1;

                _checkForMultiples = false;
            }



            if (_checkForMultiples)
            {
                if (_exactMatch)
                {

                    // Loop forward through matches

                    //DateTime _referenceTime = res.TickTime;

                    // Find the index of the last returned tick
                    int _lastIdx = Array.BinarySearch<Tick>(_ticks[_i.Year, _i.Month, _i.Day, _i.Hour], _lastReturned, _fullComp);

                    // If the last returned tick was found
                    if (_lastIdx >= 0)
                    {
                        // if the current res index is the same as the last returned or if current source is less than last returned
                        if (_sourceIndex == _lastIdx || _sourceIndex < _lastIdx)
                        {
                            // get the Tick after 'last returned'
                            if ((_lastIdx + 1) < _ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length)
                            {
                                if (_ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_lastIdx + 1].TickTime == res.TickTime)
                                    res = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_lastIdx + 1];
                            }
                        }
                    }

                }
                else
                {

                    // Loop back through matches

                    DateTime _referenceTime = res.TickTime;

                    int _loop = 0;

                    while (_referenceTime == res.TickTime)
                    {


                        // this is questionable

                        if (_lastReturned.CompareTo(_ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_sourceIndex]) == 0)
                            break;
                        

                        --_sourceIndex;

                        if (_sourceIndex < 0)
                            break;

                        if (res.TickTime == _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_sourceIndex].TickTime)
                            res = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_sourceIndex];
                        else
                            break;


                        ++_loop;

                        if ((_loop % 100) == 0)
                            InvokeErrorEvent("Inf. loop in CurrencyPairHistory GetTick()");
                    }
                }
            }

            _lastReturned = res;

            #region Old
            /*
            // Old

            // = tests Date & Rates
            // <> tests dates only

            if (false)
            {
                int _r = Array.BinarySearch<Tick>(_ticks[_i.Year, _i.Month, _i.Day, _i.Hour], _test, _comp);

                if (_r >= 0)
                {
                    // if positive, we have an exact match

                    //return _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_r];

                    res = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][_r];
                }
                else if ((~_r) < _ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length)
                {
                    // Return the value immediately prior to what we found;
                    if (~_r > 0)
                    {
                        res = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][~_r - 1];
                    }
                    else if (Time.Hour > 0)
                    {
                        res = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour - 1][_ticks[_i.Year, _i.Month, _i.Day, _i.Hour - 1].Length - 1];
                    }
                    else if (Time.Hour == 0 && _d > 0)
                    {
                        res = _ticks[_i.Year, _i.Month, _i.Day - 1, 23][_ticks[_i.Year, _i.Month, _i.Day - 1, 23].Length - 1];
                        //return _ticks[_yearMap[Time.Year], _m, _d - 1, 23][_ticks[_yearMap[Time.Year], _m, _d - 1, 23].Length - 1];
                    }
                    else if (Time.Hour == 0 && _d == 0)
                    {
                        /// fffuuuuuccckkkkk
                        res = new Tick();
                    }

                }
            }
            */
            #endregion

            return res;

        }

        public Tick[] GetHour(DateTime Time)
        {
            DateTimeArrayIndex _i = new DateTimeArrayIndex(Time, Time.Year);

            return _ticks[_i.Year, _i.Month, _i.Day, _i.Hour];

        }

        public void FindGaps()
        {

            DateTime _current = new DateTime(_start, 1, 1);

            bool _set = false;
            DateTime _lastTick = DateTime.Now;

            DateTimeArrayIndex _i = new DateTimeArrayIndex(_current, _end);

            ForexBusinessHours _forexHours = new ForexBusinessHours();

            DateTime _lastReportedDate = DateTime.Now;

            try
            {

                while (!_i.DateTraversalComplete)
                {

                    if (_forexHours.IsTradingClosed(_i.DateObject))
                    {
                        _set = false;

                        if (_i.DateObject.Year != _lastReportedDate.Year
                            || _i.DateObject.Month != _lastReportedDate.Month
                            || _i.DateObject.Day != _lastReportedDate.Day)
                            InvokeErrorEvent("Trading Closed: " + _i.DateObject.ToShortDateString());

                        _lastReportedDate = _i.DateObject;
                    }
                    else if (_ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length == 0)
                    {
                        InvokeErrorEvent("Missing Tick Data for " + _i.DateObject.ToShortDateString() + "  Is this a holiday?");
                    }
                    else
                    {
                        for (int x = 0; x < _ticks[_i.Year, _i.Month, _i.Day, _i.Hour].Length; ++x)
                        {
                            if (!_set)
                            {
                                _lastTick = _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime;
                                continue;
                            }


                            TimeSpan _dif = (_ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime - _lastTick);

                            if (_dif.TotalHours > 0.2)
                            {
                                InvokeErrorEvent("Tick Gap: " + _lastTick.ToString() + "  |  " + _ticks[_i.Year, _i.Month, _i.Day, _i.Hour][x].TickTime.ToString());
                                InvokeErrorEvent("Are these days holidays?");
                            }
                        }
                    }
                    _i.AddHours(1);
                }
            }
            catch (Exception ex)
            {
                InvokeErrorEvent(ex.Message);
            }

        }


        #region Find Column Indexes
        private int PairColumnIndex(string[] headers)
        {
            for (int i = 0; i < headers.Length; ++i)
            {
                if (headers[i].ToLower().Contains("pair"))
                    return i;
            }

            CurrencyPair _r;

            for (int i = 1; i < headers.Length; ++i)
            {
                _r = Shared.GetCurrencyPairID(Shared.NormalizeCurrencyPairName(headers[i]));

                if (_r != CurrencyPair.UNSUPPORTED)
                    return i;
            }

            return -1;
        }

        private int TickTimeColumnIndex(string[] headers)
        {
            for (int i = 0; i < headers.Length; ++i)
            {
                if (headers[i].ToLower().Contains("datetime"))
                    return i;
            }

            DateTime _r;

            for (int i = 1; i < headers.Length; ++i)
            {
                if (DateTime.TryParse(headers[i], out _r))
                    return i;
            }

            return -1;
        }

        private int RateAskColumnIndex(string[] headers)
        {
            for (int i = 0; i < headers.Length; ++i)
            {
                if (headers[i].ToLower().Contains("ask"))
                    return i;
            }

            double _r = 0.0;
            int _matchCount = 0;
            for (int i = 2; i < headers.Length; ++i)
            {
                if (Double.TryParse(headers[i], out _r))
                    ++_matchCount;

                if (_matchCount == 2)
                    return i;
            }

            return -1;
        }

        private int RateBidColumnIndex(string[] headers)
        {
            for (int i = 0; i < headers.Length; ++i)
            {
                if (headers[i].ToLower().Contains("bid"))
                    return i;
            }

            double _r = 0.0;

            for (int i = 2; i < headers.Length; ++i)
            {
                if (Double.TryParse(headers[i], out _r))
                    return i;
            }

            return -1;
        }

        #endregion



        #region Events
        protected void InvokeStatusEvent(string status)
        {
            if (OnUpdateStatus == null) return;

            ProgressEventArgs args = new ProgressEventArgs(status, this.Pair);

            OnUpdateStatus.Raise(null, args);

        }
        public delegate void StatusUpdateHandler(object sender, ProgressEventArgs e);
        public event StatusUpdateHandler OnUpdateStatus;


        protected void InvokeErrorEvent(string error)
        {
            if (OnError == null) return;

            SimulationErrorEventArgs args = new SimulationErrorEventArgs(error, this.Pair);

            OnError.Raise(null, args);
        }
        public delegate void ErrorHandler(object sender, SimulationErrorEventArgs e);
        public event ErrorHandler OnError;


        protected void InvokeLoadCompleteEvent()
        {
            if (OnLoadCompleted == null) return;

            EventArgs args = new EventArgs();


            OnLoadCompleted.Raise(this, args);
        }

        public delegate void OnLoadCompleteHandler(object sender, EventArgs e);
        public event OnLoadCompleteHandler OnLoadCompleted;
        #endregion

    }


    public class TickHistoryList : ObservableCollection<Tick>
    {
        public TickHistoryList()
            : base()
        {
        }
    }
}
